package monopoli.communication;

import java.rmi.AccessException;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.concurrent.ConcurrentSkipListSet;
import monopoli.Monopoli;

/**
 *
 * @author Marco
 */
public class Host implements HostMethods {

    public static final String INITIALIZATION_SERVER_DEFAULT_IP = "127.0.0.1";
    public static final int INITIALIZATION_SERVER_DEFAULT_PORT = 1098;

    //Attributes
    private Registry registryInitServer,  registryHost,  registryNextHost;
    private InitializationServerMethods objServer;
    private Host objHost;
    private HostMethods stubHost,  objNextHost;

    private Player myPlayer;
    private ArrayList<Player> nextPlayers;
    private HostPingPoller hPP;
    private Thread tHPP;
    private boolean token;
    private ArrayList<Message> messageSent;
    private ConcurrentSkipListSet<Long> idMessageSent;
    private long maxIdCurrentMessage;
    private MapTypeIdMessage mapTypeIdMsg;
    private boolean tokenRingFail;
    


    //Constructor
    public Host() {

        super();
        
        this.objServer = null;
        this.myPlayer = null;
        this.nextPlayers = null;
        this.token = false;
        this.messageSent = new ArrayList<Message>();
        this.idMessageSent = new ConcurrentSkipListSet<Long>();
        this.maxIdCurrentMessage = 0;
        this.mapTypeIdMsg = new MapTypeIdMessage();
        this.mapTypeIdMsg.initialize();
        this.tokenRingFail = false;
    }



    
    //Private Methods
    private boolean bindHost() {

        //Estrazione dati per il binding
        String hostIp = myPlayer.getRMIA().getIp();
        int host_port = myPlayer.getRMIA().getPort();
        String bindName = myPlayer.getRMIA().getBindName();


        //Binding dell'host
        try {
            objHost = this;
            stubHost = (HostMethods) UnicastRemoteObject.exportObject(objHost, 0);
            registryHost = LocateRegistry.getRegistry(hostIp, host_port);

            registryHost.rebind(bindName, stubHost);

            Monopoli.getMonopoliGUI().debugMessage("[DBG_com] Host: binded su IP=" + hostIp + " e porta=" + host_port + " con nome " + bindName);

        } catch (AccessException ae) {
            Monopoli.getMonopoliGUI().debugMessage("[DBG_com] Host: AccessException " + ae.getMessage());
            return false;

        } catch (RemoteException re) {
            Monopoli.getMonopoliGUI().debugMessage("[DBG_com] Host: RemoteException " + re.getMessage());
            return false;
        }

        return true;

    }

    private boolean connectToHost(RMIAddress rmiA) {


        String bindName = rmiA.getBindName();
        String hostIp = rmiA.getIp();
        int host_port = rmiA.getPort();

        try {

            registryNextHost = LocateRegistry.getRegistry(hostIp, host_port);
            objNextHost = (HostMethods) registryNextHost.lookup(bindName);

        } catch (NotBoundException nbe) {
            Monopoli.getMonopoliGUI().debugMessage("[DBG_com] NotBoundException: " + nbe.getMessage());
            return false;
        } catch (AccessException ae) {
            Monopoli.getMonopoliGUI().debugMessage("[DBG_com] AccessException: " + ae.getMessage());
            return false;
        } catch (RemoteException re) {
            Monopoli.getMonopoliGUI().debugMessage("[DBG_com] RemoteException: " + re.getMessage());
            return false;
        }

        return true;
    }

    private long generateId() {

        maxIdCurrentMessage++;
        idMessageSent.add(new Long(maxIdCurrentMessage));

        Monopoli.getMonopoliGUI().debugMessage("[DBG_com] Generato un nuovo idMsg=" + maxIdCurrentMessage);

        return maxIdCurrentMessage;
    }

    private String generateIdComplete() {

        String id;

        id = maxIdCurrentMessage + "_" + myPlayer.getRMIA().getBindName();
        
        return id;
    }

    private boolean checkMessage(Message msg) {

        int type;
        long idMessage, idCurrentMessage;


        //Recupera i valori dal messaggio
        type = msg.getType();
        if (type == Message.TYPE_NOTIFY)
            type = msg.getSubtype();
        idMessage = msg.getId();

        //Bypasso il DEATH_PLAYER
        if (type == Message.SUBTYPE_NOTIFY_DEATH_PLAYER) {

            //Aggiorna il valore nella map
            mapTypeIdMsg.update(type, idMessage);

            //Aggiorna il contatore globale
            maxIdCurrentMessage = mapTypeIdMsg.getMaxVal();

            //Aggiornamento della Debug Message
            Monopoli.getMonopoliGUI().debugMessage("[DBG_com] message con id=" + idMessage + " di type=" + type + " ?? stato ACCETTATO");

            return true;
        }


        //Interrogazione della Map
        idCurrentMessage = mapTypeIdMsg.getByType(type);

        //Confronta il valore presenta nella Map
        if (idMessage <= idCurrentMessage) {  //CASO in cui scarto il messaggio

            //Aggiornamento della Debug Message
            Monopoli.getMonopoliGUI().debugMessage("[DBG_com] message con id=" + idMessage + " di type=" + type + " ?? stato RIFIUTATO");

            return false;
        }
        else {  //CASO in cui il messaggio ?? pi?? nuovo

            //Aggiorna il valore nella map
            mapTypeIdMsg.update(type, idMessage);

            //Aggiorna il contatore globale
            maxIdCurrentMessage = mapTypeIdMsg.getMaxVal();

            //Aggiornamento della Debug Message
            Monopoli.getMonopoliGUI().debugMessage("[DBG_com] message con id=" + idMessage + " di type=" + type + " ?? stato ACCETTATO");

            return true;
        }
    }





    //Public Methods
    public boolean lookupInitializationServer() {

        try {

            String serverIp = Host.INITIALIZATION_SERVER_DEFAULT_IP;
            int server_port = Host.INITIALIZATION_SERVER_DEFAULT_PORT;
            registryInitServer = LocateRegistry.getRegistry(serverIp, server_port);
            objServer = (InitializationServerMethods) registryInitServer.lookup("MonopoliServer");

        } catch (NotBoundException nbe) {
            Monopoli.getMonopoliGUI().debugMessage("[DBG_com] NotBoundException: " + nbe.getMessage());
            return false;
        } catch (AccessException ae) {
            Monopoli.getMonopoliGUI().debugMessage("[DBG_com] AccessException: " + ae.getMessage());
            return false;
        } catch (RemoteException re) {
            Monopoli.getMonopoliGUI().debugMessage("[DBG_com] RemoteException: " + re.getMessage());
            return false;
        }

        return true;
    }

    public boolean lookupInitializationServer(String serverIp, int server_port) {

        try {

            registryInitServer = LocateRegistry.getRegistry(serverIp, server_port);
            objServer = (InitializationServerMethods) registryInitServer.lookup("MonopoliServer");


        } catch (NotBoundException nbe) {
            Monopoli.getMonopoliGUI().debugMessage("[DBG_com] NotBoundException: " + nbe.getMessage());
            return false;
        } catch (AccessException ae) {
            Monopoli.getMonopoliGUI().debugMessage("[DBG_com] AccessException: " + ae.getMessage());
            return false;
        } catch (RemoteException re) {
            Monopoli.getMonopoliGUI().debugMessage("[DBG_com] RemoteException: " + re.getMessage());
            return false;
        }

        return true;
    }

    public long pingInitializationServer() {

        try {

            Message sendMsg, recvMsg;
            Long startTime, stopTime;
            ArrayList<Object> al = null;


            startTime = new Long(System.currentTimeMillis());

            sendMsg = new Message();
            sendMsg.setId(0);
            sendMsg.setIdComplete("");
            sendMsg.setText("Ping a InitializationServer");
            sendMsg.setType(Message.TYPE_PING);
            al = new ArrayList<Object>();
            al.add(myPlayer.getRMIA().getIp());
            sendMsg.setPayload(al);


            recvMsg = objServer.ping(sendMsg);

            if (recvMsg.getType() == Message.TYPE_ERROR) {
                return -1;
            } else {
                stopTime = new Long(System.currentTimeMillis());
                return stopTime - startTime;
            }

        } catch (RemoteException re) {
            Monopoli.getMonopoliGUI().debugMessage("[DBG_com] RemoteException: " + re.getMessage());
        }

        return -1;
    }

    public long pingNextHost() {

        boolean res;

        res = connectToHost(nextPlayers.get(0).getRMIA());

        if (res) {

            try {

                Message sendMsg, recvMsg;
                Long startTime, stopTime;
                ArrayList<Object> al = null;


                startTime = new Long(System.currentTimeMillis());

                sendMsg = new Message();
                sendMsg.setId(0);
                sendMsg.setIdComplete("");
                sendMsg.setText("Richiesta di ping");
                sendMsg.setType(Message.TYPE_PING);
                al = new ArrayList<Object>();
                al.add(myPlayer.getRMIA().getBindName());
                al.add(myPlayer.getRMIA().getIp());
                sendMsg.setPayload(al);


                //Chiamata RMI
                recvMsg = objNextHost.ping(sendMsg);

                if (recvMsg.getType() == Message.TYPE_TOKEN_RING_FAIL) {

                    return Message.TYPE_TOKEN_RING_FAIL;

                }
                else {
                    stopTime = new Long(System.currentTimeMillis());
                    return stopTime - startTime;
                }

            } catch (RemoteException re) {

                //Aggiorna la Debug Console
                Monopoli.getMonopoliGUI().debugMessage("[DBG_com] RemoteException: " + re.getMessage());
            }
        }

        return Message.TYPE_PING_FAIL;
    }

    public long pingOneOfNextHost(int num) {

        boolean res;

        res = connectToHost(nextPlayers.get(num).getRMIA());

        if (res) {

            try {

                Message sendMsg, recvMsg;
                Long startTime, stopTime;
                ArrayList<Object> al = null;


                startTime = new Long(System.currentTimeMillis());

                sendMsg = new Message();
                sendMsg.setId(0);
                sendMsg.setIdComplete("");
                sendMsg.setText("Ping a nextHost");
                sendMsg.setType(Message.TYPE_PING);
                al = new ArrayList<Object>();
                al.add(myPlayer.getRMIA().getBindName());
                al.add(myPlayer.getRMIA().getIp());
                sendMsg.setPayload(al);


                //Chiamata RMI
                recvMsg = objNextHost.ping(sendMsg);

                if (recvMsg.getType() == Message.TYPE_TOKEN_RING_FAIL) {

                    return Message.TYPE_TOKEN_RING_FAIL;

                }
                else {
                    stopTime = new Long(System.currentTimeMillis());
                    return stopTime - startTime;
                }

            } catch (RemoteException re) {

                //Aggiorna la Debug Console
                Monopoli.getMonopoliGUI().debugMessage("[DBG_com] RemoteException: " + re.getMessage());
            }
        }

        return Message.TYPE_PING_FAIL;
    }

    public Player getMyPlayer() {
        return myPlayer;
    }

    public boolean setMyPlayer(Player myPlayer) {
        
        this.myPlayer = myPlayer;
        boolean res = bindHost();

        return res;
    }

    public ArrayList<Player> getNextPlayers() {
        return nextPlayers;
    }

    public void setNextPlayers(ArrayList<Player> nextPlayers) {
        synchronized(nextPlayers) {
            this.nextPlayers = nextPlayers;
        }
    }

    synchronized public boolean removeNextPlayer(int idGiocatore) {

        synchronized(nextPlayers) {
            Iterator<Player> it = nextPlayers.iterator();
            boolean ok = false;
            int i = 0;

            while (it.hasNext() && !ok) {
                Player p = it.next();
                if (p.getIdGiocatore() == idGiocatore)
                    ok = true;
                else
                    i++;
            }

            if (ok) {
                nextPlayers.remove(i);
                return true;
            }

            return false;
        }
    }

    synchronized public void updateToken(boolean val) {
        this.token = val;
    }

    synchronized public void updateTokenRingFail(boolean val) {
        this.tokenRingFail = val;
    }

    public boolean isToken() {
        if (token)
            return true;
        else
            return false;
    }

    public boolean isTokenRingFail() {
        return tokenRingFail;
    }
    
    public boolean requestGame(Integer idGiocatore) {

        Message recvMsg, sendMsg;
        ArrayList<Object> al = null;
        String strMsg = null;

        try {

            //Request al server
            sendMsg = new Message();
            sendMsg.setId(0);
            sendMsg.setIdComplete("");
            sendMsg.setText("");
            sendMsg.setType(Message.TYPE_REQUEST);
            al = new ArrayList<Object>();
            al.add(idGiocatore.toString());
            al.add(myPlayer.getRMIA());
            sendMsg.setPayload(al);

            //Invio chiamata RMI
            recvMsg = objServer.request(sendMsg);


            if (recvMsg.getType() == Message.TYPE_REQUEST) {
                strMsg = "[DBG_com] Host: " + recvMsg.getText();
            } else if (recvMsg.getType() == Message.TYPE_ERROR) {
                strMsg = "[DBG_com] Host: " + recvMsg.getText();
            } else {
                strMsg = "[DBG_com] Host: ricevuto messaggio non riconosciuto";
            }

            Monopoli.getMonopoliGUI().debugMessage(strMsg);
        } catch (RemoteException re) {
            Monopoli.getMonopoliGUI().debugMessage("[DBG_com] Host: RemoteException: " + re.getMessage());
            return false;
        }

        return true;

    }

    public boolean sendTkn() {

        boolean res;

        //Imposta la connessione
        res = connectToHost(nextPlayers.get(0).getRMIA());

        if (res) {
            try {

                Message sendMsg, recvMsg;

                sendMsg = new Message();
                sendMsg.setId(generateId());
                sendMsg.setIdComplete(generateIdComplete());
                sendMsg.setText("Richiesta di sendToken");
                sendMsg.setType(Message.TYPE_TOKEN);
                sendMsg.setPayload(null);

                //Salvataggio del messaggio e dell'id nella lista dei messaggi inviati
                idMessageSent.add(new Long(sendMsg.getId()));
                synchronized(messageSent) {
                    messageSent.add(sendMsg);
                }

                ////////////////////////////////////////////////////////////////////////////
                Monopoli.getMonopoliGUI().debugMessage("[DBG_com] Inviato messaggio con id=" + maxIdCurrentMessage);
                ////////////////////////////////////////////////////////////////////////////

                //Invocazione metodo RMI
                recvMsg = objNextHost.sendToken(sendMsg);

                if (recvMsg.getType() == Message.TYPE_ERROR) {
                    return false;
                } else {
                    updateToken(false);
                    return true;
                }

            } catch (RemoteException re) {
                Monopoli.getMonopoliGUI().debugMessage("[DBG_com] RemoteException: " + re.getMessage());

                //Avvia il thread di RecoverFail
                Thread tRecoverFail = new RecoverFail(this);
                tRecoverFail.setName("RECOVER_FromRmiCallFail");
                tRecoverFail.start();

                return false;
            }
        }
        return true;
    }

    public boolean sendMsg(int type, int subtype, long idMsg, ArrayList<Object> payload, int tour) {

        Message sendMsg, recvMsg;
        boolean res;
        String idCompleteMsg = null;

        //Imposta la connessione
        res = connectToHost(nextPlayers.get(0).getRMIA());

        if (res) {

            try {

                //Creazione messaggio da inviare
                sendMsg = new Message();

                //Controllo se il parametro idMessage ?? uguale a 0
                if (idMsg == 0) {
                    idMsg = generateId();
                    idCompleteMsg = generateIdComplete();
                }

                
                //Impostazione messaggio da inviare in base al tipo
                if (type == Message.TYPE_NOTIFY) {

                   
                    switch (subtype) {

                        case Message.SUBTYPE_NOTIFY_MOVE_PEDINA:
                            sendMsg.setId(idMsg);
                            sendMsg.setIdComplete(idCompleteMsg);
                            sendMsg.setText("Notifica: muovi pedina");
                            sendMsg.setType(Message.TYPE_NOTIFY);
                            sendMsg.setSubtype(Message.SUBTYPE_NOTIFY_MOVE_PEDINA);
                            sendMsg.setTour(tour);
                            sendMsg.setPayload(payload);
                            break;

                        case Message.SUBTYPE_NOTIFY_PEDINA:
                            sendMsg.setId(idMsg);
                            sendMsg.setIdComplete(idCompleteMsg);
                            sendMsg.setText("Notifica: pedina");
                            sendMsg.setType(Message.TYPE_NOTIFY);
                            sendMsg.setSubtype(Message.SUBTYPE_NOTIFY_PEDINA);
                            sendMsg.setTour(tour);
                            sendMsg.setPayload(payload);
                            break;

                        case Message.SUBTYPE_NOTIFY_NAME:
                            sendMsg.setId(idMsg);
                            sendMsg.setIdComplete(idCompleteMsg);
                            sendMsg.setText("Notifica: nome");
                            sendMsg.setType(Message.TYPE_NOTIFY);
                            sendMsg.setSubtype(Message.SUBTYPE_NOTIFY_NAME);
                            sendMsg.setTour(tour);
                            sendMsg.setPayload(payload);
                            break;

                        case Message.SUBTYPE_NOTIFY_GET_PROPERTY:
                            sendMsg.setId(idMsg);
                            sendMsg.setIdComplete(idCompleteMsg);
                            sendMsg.setText("Notifica: possesso propriet??");
                            sendMsg.setType(Message.TYPE_NOTIFY);
                            sendMsg.setSubtype(Message.SUBTYPE_NOTIFY_GET_PROPERTY);
                            sendMsg.setTour(tour);
                            sendMsg.setPayload(payload);
                            break;

                        case Message.SUBTYPE_NOTIFY_BUY_PROPERTY:
                            sendMsg.setId(idMsg);
                            sendMsg.setIdComplete(idCompleteMsg);
                            sendMsg.setText("Notifica: acquisto proprieta");
                            sendMsg.setType(Message.TYPE_NOTIFY);
                            sendMsg.setSubtype(Message.SUBTYPE_NOTIFY_BUY_PROPERTY);
                            sendMsg.setTour(tour);
                            sendMsg.setPayload(payload);
                            break;

                        case Message.SUBTYPE_NOTIFY_BUILD_BUILDING:
                            sendMsg.setId(idMsg);
                            sendMsg.setIdComplete(idCompleteMsg);
                            sendMsg.setText("Notifica: costruzione edificio");
                            sendMsg.setType(Message.TYPE_NOTIFY);
                            sendMsg.setSubtype(Message.SUBTYPE_NOTIFY_BUILD_BUILDING);
                            sendMsg.setTour(tour);
                            sendMsg.setPayload(payload);
                            break;

                        case Message.SUBTYPE_NOTIFY_PAYMENT:
                            sendMsg.setId(idMsg);
                            sendMsg.setIdComplete(idCompleteMsg);
                            sendMsg.setText("Notifica: pagamento");
                            sendMsg.setType(Message.TYPE_NOTIFY);
                            sendMsg.setSubtype(Message.SUBTYPE_NOTIFY_PAYMENT);
                            sendMsg.setTour(tour);
                            sendMsg.setPayload(payload);
                            break;

                        case Message.SUBTYPE_NOTIFY_MSG:
                            sendMsg.setId(idMsg);
                            sendMsg.setIdComplete(idCompleteMsg);
                            sendMsg.setText("Notifica: messaggio");
                            sendMsg.setType(Message.TYPE_NOTIFY);
                            sendMsg.setSubtype(Message.SUBTYPE_NOTIFY_MSG);
                            sendMsg.setTour(tour);
                            sendMsg.setPayload(payload);
                            break;


                        case Message.SUBTYPE_NOTIFY_WITHDRAWAL:
                            sendMsg.setId(idMsg);
                            sendMsg.setIdComplete(idCompleteMsg);
                            sendMsg.setText("Notifica: ritiro");
                            sendMsg.setType(Message.TYPE_NOTIFY);
                            sendMsg.setSubtype(Message.SUBTYPE_NOTIFY_WITHDRAWAL);
                            sendMsg.setTour(tour);
                            sendMsg.setPayload(payload);
                            break;

                        case Message.SUBTYPE_NOTIFY_MORTGAGE:
                            sendMsg.setId(idMsg);
                            sendMsg.setIdComplete(idCompleteMsg);
                            sendMsg.setText("Notifica: ipoteca");
                            sendMsg.setType(Message.TYPE_NOTIFY);
                            sendMsg.setSubtype(Message.SUBTYPE_NOTIFY_MORTGAGE);
                            sendMsg.setTour(tour);
                            sendMsg.setPayload(payload);
                            break;

                        case Message.SUBTYPE_NOTIFY_END_GAME:
                            sendMsg.setId(idMsg);
                            sendMsg.setIdComplete(idCompleteMsg);
                            sendMsg.setText("Notifica: fine gioco");
                            sendMsg.setType(Message.TYPE_NOTIFY);
                            sendMsg.setSubtype(Message.SUBTYPE_NOTIFY_END_GAME);
                            sendMsg.setTour(tour);
                            sendMsg.setPayload(payload);
                            break;

                        case Message.SUBTYPE_NOTIFY_AUCTION_STARTED:
                            sendMsg.setId(idMsg);
                            sendMsg.setIdComplete(idCompleteMsg);
                            sendMsg.setText("Notifica: inizio asta");
                            sendMsg.setType(Message.TYPE_NOTIFY);
                            sendMsg.setSubtype(Message.SUBTYPE_NOTIFY_AUCTION_STARTED);
                            sendMsg.setTour(tour);
                            sendMsg.setPayload(payload);
                            break;

                        case Message.SUBTYPE_NOTIFY_DEATH_PLAYER:
                            sendMsg.setId(idMsg);
                            sendMsg.setIdComplete(idCompleteMsg);
                            sendMsg.setText("Notifica: caduta giocatore");
                            sendMsg.setType(Message.TYPE_NOTIFY);
                            sendMsg.setSubtype(Message.SUBTYPE_NOTIFY_DEATH_PLAYER);
                            sendMsg.setTour(tour);
                            sendMsg.setPayload(payload);
                            break;

                        default:
                            break;
                    }
                }



                //Salvataggio del messaggio e idMessaggio nella hashMap
                idMessageSent.add(new Long(sendMsg.getId()));
                synchronized(messageSent) {
                    messageSent.add(sendMsg);
                }

                type = sendMsg.getType();
                if (type == Message.TYPE_NOTIFY)
                    type = sendMsg.getSubtype();
                long idMessage = sendMsg.getId();

                mapTypeIdMsg.update(type, idMessage);


                //Invocazione metodo RMI
                recvMsg = objNextHost.sendMessage(sendMsg);

                if (recvMsg.getType() == Message.TYPE_ERROR) {
                    Monopoli.getMonopoliGUI().debugMessage("[DBG_com] Errore nel tipo di messaggio");
                    return false;
                }

                ////////////////////////////////////////////////////////////////////////////
                Monopoli.getMonopoliGUI().debugMessage("[DBG_com] Inviato messaggio con id=" + maxIdCurrentMessage);
                ////////////////////////////////////////////////////////////////////////////

            } catch (RemoteException re) {
                Monopoli.getMonopoliGUI().debugMessage("[DBG_com] RemoteException: " + re.getMessage());

                //Avvia il thread di RecoverFail
                Thread tRecoverFail = new RecoverFail(this);
                tRecoverFail.setName("RECOVER_FromRmiCallFail");
                tRecoverFail.start();

                return false;
            }
        }

        return true;
    }

    synchronized public boolean sendAllCachedMsg() {

        Message sendMsg, recvMsg;
        boolean res;
        

        //Imposta la connessione
        res = connectToHost(nextPlayers.get(0).getRMIA());

        if (res) {
            Iterator<Message> it;
            synchronized(messageSent) {
                it = messageSent.iterator();
                while ( it.hasNext() ) {

                    sendMsg = it.next();

                    try {

                        //Invocazione metodo RMI appropriato
                        if (sendMsg.getType() == Message.TYPE_TOKEN)
                            recvMsg = objNextHost.sendToken(sendMsg);
                        else
                            recvMsg = objNextHost.sendMessage(sendMsg);

                    } catch (RemoteException re) {
                        Monopoli.getMonopoliGUI().debugMessage("[DBG_com] RemoteException: " + re.getMessage());
                        return false;
                    }

                    if (recvMsg.getType() == Message.TYPE_ERROR) {
                        Monopoli.getMonopoliGUI().debugMessage("[DBG_com] Errore nel tipo di messaggio");
                        return false;
                    }
                }
            }
        }

        return true;

    }








    

    //Metodi RMI chiamati da InitializationServer
    @Override
    public void rifiutaPartecipaPartita() {

        //Passaggio chiamata a livello GAME
        Monopoli.getMonopoliPartita().getGameToCommunication().rifiutaPartecipaPartita("Partita rifiutata");
    }

    @Override
    public void confermaPartecipaPartita(int idOrdine, ArrayList<Player> nextPlayers) {

        //Settaggio della lista dei Players
        this.setNextPlayers(nextPlayers);


        //Aggiorna la HostTab
        String localHostBindName = this.myPlayer.getRMIA().getBindName();
        String localHostIp = this.myPlayer.getRMIA().getIp();
        String localHostPort = Integer.toString(this.myPlayer.getRMIA().getPort());
        String nextHostBindName = this.nextPlayers.get(0).getRMIA().getBindName();
        String nextHostIp = this.nextPlayers.get(0).getRMIA().getIp();
        String nextHostPort = Integer.toString(this.nextPlayers.get(0).getRMIA().getPort());

        Monopoli.getMonopoliGUI().getGestoreRefresh().refreshHostData(localHostBindName, localHostIp, localHostPort, nextHostBindName, nextHostIp, nextHostPort);



        //Aggiorna lo stato di token
        if (idOrdine == 1) {
            updateToken(true);
        } else {
            updateToken(false);
        }


        //Avvio di HostPingPoller
        hPP = new HostPingPoller(this);
        tHPP = new Thread(hPP);
        tHPP.start();


        
        //Passaggio chiamata a livello GAME
        Monopoli.getMonopoliPartita().getGameToCommunication().confermaPartecipaPartita(idOrdine);
    }








    //Metodi RMI chiamati da Host
    @Override
    public Message ping(Message msg) throws RemoteException {

        Message retMsg = null;

        //Recupero dati del messaggio
        String bindName = (String) msg.getPayload().get(0);
        String ipHost = (String) msg.getPayload().get(1);

        //Aggiornamento della DebugMessage
        Monopoli.getMonopoliGUI().debugMessage("[DBG_com] Invocked Method ping(): bindName=" + bindName + " ip=" + ipHost);

        //Verifica stato della tokenRing
        if ( !tokenRingFail ) { //CASO in cui tokenRing funzionante

            //Costruzione messaggio di risposta
            retMsg = new Message();
            retMsg.setId(0);
            retMsg.setIdComplete("");
            retMsg.setText("Host con Ip=" + getMyPlayer().getRMIA().getIp() + " porta=" + getMyPlayer().getRMIA().getPort() + " e con nome " + getMyPlayer().getRMIA().getBindName());
            retMsg.setType(Message.TYPE_PING);
            retMsg.setPayload(null);
        }
        else {  //CASO in cui la tokenRing ?? corrotta

            //Costruzione messaggio di risposta
            retMsg = new Message();
            retMsg.setId(0);
            retMsg.setIdComplete("");
            retMsg.setText("TOKEN_RING_FAIL");
            retMsg.setType(Message.TYPE_TOKEN_RING_FAIL);
            retMsg.setPayload(null);
        }
        

        return retMsg;
    }

    @Override
    public Message sendToken(Message msg) throws RemoteException {

        Message retMsg = null;
        boolean ok;

        //Controlla il messaggio
        ok = checkMessage(msg);
        if ( !ok ) {  //CASO messaggio ignorato

            //Costruzione messaggio di risposta
            retMsg = new Message();
            retMsg.setId(msg.getId());
            retMsg.setIdComplete(msg.getIdComplete());
            retMsg.setText("TOKEN IGNORED");
            retMsg.setType(Message.TYPE_TOKEN);
            retMsg.setPayload(null);

            return retMsg;
        }
        else {  //CASO messaggio accettato

            //Aggiorna la variabile token
            updateToken(true);

            //Pulisco la lista dei messaggi inviati
            synchronized(messageSent) {
                messageSent.clear();
            }
            
            //Invia informaziozione a livello game
            Monopoli.getMonopoliPartita().getGameToCommunication().iniziaTurno();

            //Aggiornamento della DebugMessage
            Monopoli.getMonopoliGUI().debugMessage("[DBG_com] Invocked Method sendToken()");

            //Costruzione messaggio di risposta
            retMsg = new Message();
            retMsg.setId(msg.getId());
            retMsg.setIdComplete(msg.getIdComplete());
            retMsg.setText(myPlayer.getRMIA().getBindName() + " ha ricevuto il token");
            retMsg.setType(Message.TYPE_TOKEN);
            retMsg.setPayload(null);

            return retMsg;
        }
    }

    @Override
    public Message sendMessage(Message msg) throws RemoteException {

        Message retMsg = null;
        ArrayList<Object> payload = null;
        int giocatore;
        boolean ok;

        retMsg = new Message();


        //Controlla che l'idMessage sia maggiore idCurrentMessage
        ok = checkMessage(msg);

        if ( !ok ) {  //CASO messaggio ignorato
            
            //Preparazione messaggio di ritorno
            retMsg.setId(msg.getId());
            retMsg.setIdComplete(msg.getIdComplete());
            retMsg.setText("MESSAGE IGNORED");
            retMsg.setType(msg.getType());
            retMsg.setSubtype(msg.getSubtype());
            retMsg.setPayload(null);

            return retMsg;
        }
        
        //CASO messaggio accettato

        //Impostazione risposta al Messaggio in base al tipo
        if (msg.getType() == Message.TYPE_NOTIFY) {


            //Gestione inoltro messaggio
            long idMessage = msg.getId();

            if (idMessageSent.contains(new Long(idMessage))) {  //CASO in cui sono il creatore del messaggio


                if (msg.getTour()-1 == 0) {   //SOTTOCASO in cui tour=0 e devo rimuovere il messaggio dall'anello

                    //Rimuove il idMessage dall'elenco dei idMessage inviati
                    idMessageSent.remove(idMessage);

                    //Aggiornamento della DebugConsole
                    Monopoli.getMonopoliGUI().debugMessage("[DBG_com] " + myPlayer.getRMIA().getBindName() + " ha rimosso il msg con id=" + idMessage + "dall'anello");

                    retMsg.setId(-1);
                    retMsg.setIdComplete("");
                    retMsg.setText("");
                    retMsg.setType(msg.getType());
                    retMsg.setSubtype(msg.getSubtype());
                    retMsg.setPayload(null);
                    return retMsg;
                }
                else {  //SOTTOCASO in cui tour viene decrementato e il messaggio viene inoltrato nell'anello
                    sendMsg(msg.getType(), msg.getSubtype(), msg.getId(), msg.getPayload(), msg.getTour() - 1);
                    Monopoli.getMonopoliGUI().debugMessage("[DBG_com] " + myPlayer.getRMIA().getBindName() + " ha decrementato tour nel msg e lo ha inoltrato");
                }


            } else {  //CASO in cui non sono il creatore del messaggio
                sendMsg(msg.getType(), msg.getSubtype(), msg.getId(), msg.getPayload(), msg.getTour());
                Monopoli.getMonopoliGUI().debugMessage("[DBG_com] " + myPlayer.getRMIA().getBindName() + " ha inoltrato il messaggio nell'anello");
            }



            

            //Gestione risposta al messaggio in base al sottotipo
            switch (msg.getSubtype()) {

                case Message.SUBTYPE_NOTIFY_MOVE_PEDINA:

                    retMsg.setId(-1);
                    retMsg.setIdComplete("");
                    retMsg.setText(myPlayer.getRMIA().getBindName() + " ha ricevuto notificaMuoviPedina");
                    retMsg.setType(Message.TYPE_NOTIFY);
                    retMsg.setSubtype(Message.SUBTYPE_NOTIFY_MOVE_PEDINA);
                    retMsg.setPayload(null);

                    //Ricava dati dal payload
                    int pedina = ((Integer)msg.getPayload().get(0)).intValue();
                    int casella = ((Integer)msg.getPayload().get(1)).intValue();
                    Monopoli.getMonopoliPartita().getGameToCommunication().comunicaMovimentoPedina(pedina,casella);
                    Monopoli.getMonopoliGUI().debugMessage("[DBG_com] " + myPlayer.getRMIA().getBindName() + " ha ricevuto notificaMuoviPedina");

                    break;

                 case Message.SUBTYPE_NOTIFY_PEDINA:

                    retMsg.setId(-1);
                    retMsg.setIdComplete("");
                    retMsg.setText(myPlayer.getRMIA().getBindName() + " ha ricevuto notificaPedina");
                    retMsg.setType(Message.TYPE_NOTIFY);
                    retMsg.setSubtype(Message.SUBTYPE_NOTIFY_PEDINA);
                    retMsg.setPayload(null);

                    //ricavo dati dal payload
                    giocatore = ((Integer)msg.getPayload().get(0)).intValue();
                    int idPedina = ((Integer)msg.getPayload().get(1)).intValue();
                    Monopoli.getMonopoliPartita().getGameToCommunication().comunicaPedina(giocatore,idPedina);
                    Monopoli.getMonopoliGUI().debugMessage("[DBG_com] " + myPlayer.getRMIA().getBindName() + " ha ricevuto notificaPedina");

                    break;

                case Message.SUBTYPE_NOTIFY_NAME:

                    retMsg.setId(-1);
                    retMsg.setIdComplete("");
                    retMsg.setText(myPlayer.getRMIA().getBindName() + " ha ricevuto notificaName");
                    retMsg.setType(Message.TYPE_NOTIFY);
                    retMsg.setSubtype(Message.SUBTYPE_NOTIFY_NAME);
                    retMsg.setPayload(null);

                    //Ricava dati dal payload
                    giocatore = ((Integer)msg.getPayload().get(0)).intValue();
                    String name = ((String)msg.getPayload().get(1));
                    
                    Monopoli.getMonopoliPartita().getGameToCommunication().comunicaNome(giocatore,name);
                    Monopoli.getMonopoliGUI().debugMessage("[DBG_com] " + myPlayer.getRMIA().getBindName() + " ha ricevuto notificaNome");

                    break;

                case Message.SUBTYPE_NOTIFY_GET_PROPERTY:

                    retMsg.setId(-1);
                    retMsg.setIdComplete("");
                    retMsg.setText(myPlayer.getRMIA().getBindName() + " ha ricevuto notificaPossessoProprieta");
                    retMsg.setType(Message.TYPE_NOTIFY);
                    retMsg.setSubtype(Message.SUBTYPE_NOTIFY_GET_PROPERTY);
                    retMsg.setPayload(null);

                    //Ricava dati dal payload
                    giocatore = ((Integer)msg.getPayload().get(0)).intValue();
                    int idProprieta = ((Integer)msg.getPayload().get(1)).intValue();
                    Monopoli.getMonopoliPartita().getGameToCommunication().comunicaPossessoProprieta(giocatore,idProprieta);
                    Monopoli.getMonopoliGUI().debugMessage("[DBG_com] " + myPlayer.getRMIA().getBindName() + " ha ricevuto notificaPossessoPropriet??");

                    break;

                case Message.SUBTYPE_NOTIFY_BUY_PROPERTY:

                    retMsg.setId(-1);
                    retMsg.setIdComplete("");
                    retMsg.setText(myPlayer.getRMIA().getBindName() + " ha ricevuto notificaAcquistoProprieta");
                    retMsg.setType(Message.TYPE_NOTIFY);
                    retMsg.setSubtype(Message.SUBTYPE_NOTIFY_BUY_PROPERTY);
                    retMsg.setPayload(null);

                    //Ricava dati dal payload
                    giocatore = ((Integer)msg.getPayload().get(0)).intValue();
                    idProprieta = ((Integer)msg.getPayload().get(1)).intValue();
                    Monopoli.getMonopoliPartita().getGameToCommunication().comunicaAcquistoProprieta(giocatore,idProprieta);
                    Monopoli.getMonopoliGUI().debugMessage("[DBG_com] " + myPlayer.getRMIA().getBindName() + " ha ricevuto notificaAcquistoPropriet??");

                    break;

                case Message.SUBTYPE_NOTIFY_BUILD_BUILDING:

                    retMsg.setId(-1);
                    retMsg.setIdComplete("");
                    retMsg.setText(myPlayer.getRMIA().getBindName() + " ha ricevuto notificaCostruzioneEdificio");
                    retMsg.setType(Message.TYPE_NOTIFY);
                    retMsg.setSubtype(Message.SUBTYPE_NOTIFY_BUILD_BUILDING);
                    retMsg.setPayload(null);

                    //Ricava dati dal payload
                    giocatore = ((Integer)msg.getPayload().get(0)).intValue();
                    int idCasella = ((Integer)msg.getPayload().get(1)).intValue();
                    int edificio = ((Integer)msg.getPayload().get(2)).intValue();
                    Monopoli.getMonopoliPartita().getGameToCommunication().comunicaCostruzioneEdificio(giocatore,idCasella,edificio);
                    Monopoli.getMonopoliGUI().debugMessage("[DBG_com] " + myPlayer.getRMIA().getBindName() + " ha ricevuto notificaCostruzioneEdificio");
                    break;

                case Message.SUBTYPE_NOTIFY_PAYMENT:

                    retMsg.setId(-1);
                    retMsg.setIdComplete("");
                    retMsg.setText(myPlayer.getRMIA().getBindName() + " ha ricevuto notificaPagamento");
                    retMsg.setType(Message.TYPE_NOTIFY);
                    retMsg.setSubtype(Message.SUBTYPE_NOTIFY_PAYMENT);
                    retMsg.setPayload(null);

                    //Ricava dati dal payload
                    int amount = ((Integer)msg.getPayload().get(0)).intValue();
                    int giocatore1 = ((Integer)msg.getPayload().get(1)).intValue();
                    int giocatore2 = ((Integer)msg.getPayload().get(2)).intValue();
                    Monopoli.getMonopoliPartita().getGameToCommunication().comunicaPagamento(amount,giocatore1,giocatore2);
                    Monopoli.getMonopoliGUI().debugMessage("[DBG_com] " + myPlayer.getRMIA().getBindName() + " ha ricevuto notificaPagamento");
                    break;

                case Message.SUBTYPE_NOTIFY_WITHDRAWAL:

                    retMsg.setId(-1);
                    retMsg.setIdComplete("");
                    retMsg.setText(myPlayer.getRMIA().getBindName() + " ha ricevuto notificaRitiro");
                    retMsg.setType(Message.TYPE_NOTIFY);
                    retMsg.setSubtype(Message.SUBTYPE_NOTIFY_WITHDRAWAL);
                    retMsg.setPayload(null);

                    Monopoli.getMonopoliPartita().getGameToCommunication().comunicaRitiro();
                    Monopoli.getMonopoliGUI().debugMessage("[DBG_com] " + myPlayer.getRMIA().getBindName() + " ha ricevuto notificaRitiro");
                    break;

                case Message.SUBTYPE_NOTIFY_MORTGAGE:

                    retMsg.setId(-1);
                    retMsg.setIdComplete("");
                    retMsg.setText(myPlayer.getRMIA().getBindName() + " ha ricevuto notificaIpoteca");
                    retMsg.setType(Message.TYPE_NOTIFY);
                    retMsg.setSubtype(Message.SUBTYPE_NOTIFY_MORTGAGE);
                    retMsg.setPayload(null);

                    Monopoli.getMonopoliPartita().getGameToCommunication().comunicaIpoteca();
                    Monopoli.getMonopoliGUI().debugMessage("[DBG_com] " + myPlayer.getRMIA().getBindName() + " ha ricevuto notificaIpoteca");
                    break;

                case Message.SUBTYPE_NOTIFY_END_GAME:

                    retMsg.setId(-1);
                    retMsg.setIdComplete("");
                    retMsg.setText(myPlayer.getRMIA().getBindName() + " ha ricevuto notificaFinePartita");
                    retMsg.setType(Message.TYPE_NOTIFY);
                    retMsg.setSubtype(Message.SUBTYPE_NOTIFY_END_GAME);
                    retMsg.setPayload(null);

                    Monopoli.getMonopoliPartita().getGameToCommunication().comunicaFinePartita();
                    Monopoli.getMonopoliGUI().debugMessage("[DBG_com] " + myPlayer.getRMIA().getBindName() + " ha ricevuto notificaFinePartita");
                    break;

                case Message.SUBTYPE_NOTIFY_AUCTION_STARTED:

                    retMsg.setId(-1);
                    retMsg.setIdComplete("");
                    retMsg.setText(myPlayer.getRMIA().getBindName() + " ha ricevuto notificaAstaAvviata");
                    retMsg.setType(Message.TYPE_NOTIFY);
                    retMsg.setSubtype(Message.SUBTYPE_NOTIFY_AUCTION_STARTED);
                    retMsg.setPayload(null);

                    Monopoli.getMonopoliPartita().getGameToCommunication().comunicaAstaAvviata();
                    Monopoli.getMonopoliGUI().debugMessage("[DBG_com] " + myPlayer.getRMIA().getBindName() + " ha ricevuto notificaAstaAvviata");
                    break;

                case Message.SUBTYPE_NOTIFY_DEATH_PLAYER:

                    //Preparazione messaggio di ritorno
                    retMsg.setId(-1);
                    retMsg.setIdComplete("");
                    retMsg.setText(myPlayer.getRMIA().getBindName() + " ha ricevuto notificaCadutaGiocatore");
                    retMsg.setType(Message.TYPE_NOTIFY);
                    retMsg.setSubtype(Message.SUBTYPE_NOTIFY_DEATH_PLAYER);
                    retMsg.setPayload(null);


                    //Ricava dati dal payload
                    String bindName = (String)msg.getPayload().get(0);
                    int idGiocatore = ((Integer)msg.getPayload().get(1)).intValue();


                    //Aggiorna la lista dei nextPlayers
                    synchronized(nextPlayers) {
                        Iterator<Player> it = nextPlayers.iterator();
                        boolean stop = false;
                        int j = 0;
                        while (it.hasNext() && !stop) {
                            RMIAddress rmiA = it.next().getRMIA();

                            if(rmiA.getBindName().compareTo(bindName) == 0) {
                                nextPlayers.remove(j);
                                stop = true;
                            }
                            j++;
                        }
                    }



                    

                    //Invio notifica a livello game
                    Monopoli.getMonopoliPartita().getGameToCommunication().comunicaCadutaGiocatore(idGiocatore);


                    //Invio messaggio nella DebugConsole
                    Monopoli.getMonopoliGUI().debugMessage("[DBG_com] " + myPlayer.getRMIA().getBindName() + " ha ricevuto notificaCadutaGiocatore del idGiocatore=" + idGiocatore);
                    break;

                case Message.SUBTYPE_NOTIFY_MSG:

                    retMsg.setId(-1);
                    retMsg.setIdComplete("");
                    retMsg.setText(myPlayer.getRMIA().getBindName() + " ha ricevuto notificaMessaggio");
                    retMsg.setType(Message.TYPE_NOTIFY);
                    retMsg.setSubtype(Message.SUBTYPE_NOTIFY_MSG);
                    retMsg.setPayload(null);

                     //ricavo dati dal payload
                    String s = ((String)msg.getPayload().get(0));
                    Monopoli.getMonopoliPartita().getGameToCommunication().comunicaMsg(s);
                    Monopoli.getMonopoliGUI().debugMessage("[DBG_com] " + myPlayer.getRMIA().getBindName() + " ha ricevuto notificaMessaggio");
                    break;

                default:
                    break;
            }

        }

        

        return retMsg;
    }
}


